using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using AzureContrib.WindowsAzure.Diagnostics;
using AzureContrib.WindowsAzure.Properties;
using AzureContrib.WindowsAzure.ServiceRuntime;
using AzureContrib.WindowsAzure.Utils;
using Microsoft.WindowsAzure;

namespace AzureContrib.WindowsAzure.StorageClient
{
	/// <summary>
	/// A registry for cloud storage accounts used to abstract away specific accounts from the underlying code.
	/// </summary>
	/// <remarks><see cref="ICloudStorageAccountManager.AddCloudStorageAccount(string,ICloudStorageAccount)"/> is used during app start to register the application data contexts.</remarks>
	/// <remarks><see cref="ICloudStorageAccountManager.GetStorageAccount"/> is used in the app to fetch <see cref="ICloudStorageAccount"/>s given a certain context.</remarks>
	public class CloudStorageAccountManager : ICloudStorageAccountManager
	{
		internal IDictionary<string, ICloudStorageAccount> accountContexts;
		private ITrace trace;
		private readonly IRoleEnvironment roleEnvironment;

		/// <summary>
		/// Create a new <see cref="CloudStorageAccountManager"/>
		/// </summary>
		/// <param name="trace"><see cref="ITrace"/></param>
		/// <param name="roleEnvironment"><see cref="IRoleEnvironment"/></param>
		public CloudStorageAccountManager(ITrace trace, IRoleEnvironment roleEnvironment)
		{
			this.trace = trace;
			this.roleEnvironment = roleEnvironment;
			accountContexts = new ConcurrentDictionary<string, ICloudStorageAccount>();
		}

		/// <summary>
		/// <see cref="ICloudStorageAccountManager.AddCloudStorageAccount(string,ICloudStorageAccount)"/>
		/// </summary>
		public void AddCloudStorageAccount(string context, ICloudStorageAccount cloudStorageAccount)
		{
			Assumes.NotNullOrEmpty(context, "context", Strings.ContextHasToBePresent);
			accountContexts.Add(context, cloudStorageAccount);
		}

		/// <summary>
		/// <see cref="ICloudStorageAccountManager.AddCloudStorageAccount(string,string)"/>
		/// </summary>
		public void AddCloudStorageAccount(string context, string connectionString)
		{
			CloudStorageAccount account;
			if (!CloudStorageAccount.TryParse(connectionString, out account))
			{
				throw new ArgumentOutOfRangeException("connectionString", Strings.Invalid_CloudStorageAccount_ConnectionString);
			}
			
			AddCloudStorageAccount(context, account.Wrap());
		}

		/// <summary>
		/// <see cref="ICloudStorageAccountManager.GetStorageAccount"/>
		/// </summary>
		public ICloudStorageAccount GetStorageAccount(string context)
		{
			Assumes.NotNullOrEmpty(context, "context", Strings.ContextHasToBePresent);

			if (!accountContexts.ContainsKey(context))
			{
				TryToLoadAccountUsingContextAsConfigKey(context);
			}

			if (!accountContexts.ContainsKey(context))
			{
				var message = Strings.UnableToRetrieveCloudStoreageAccountForContext.F(context);
				trace.Error(message);
				throw new ArgumentException(message, "context");
			}

			var cloudStorageAccount = accountContexts[context];

			return cloudStorageAccount;
		}

		private void TryToLoadAccountUsingContextAsConfigKey(string settingName)
		{
			string settingValue;
			if (!roleEnvironment.TryGetConfigurationSettingValue(settingName, out settingValue))
			{
				return;
			}

			CloudStorageAccount cloudStorageAccount;
			if (CloudStorageAccount.TryParse(settingValue, out cloudStorageAccount))
			{
				accountContexts.Add(settingName, cloudStorageAccount.Wrap());
			}
		}
	}
}